package com.hmdp.test.lifecycle.ext;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.*;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
import org.springframework.web.context.ServletContextAware;

import javax.servlet.ServletContext;

/**
 * 容器属性其实就是容器自带的属性，这些属性都是spring本来就有的；可以肯定的是，它们都是 Aware 接口的实现类，主要有以下实现类
 *
 * @author lizhihui
 */
@Slf4j
@Component
public class CMyAllAwareInterface implements
        BeanNameAware,
        BeanClassLoaderAware,
        BeanFactoryAware,
        EnvironmentAware,
        EmbeddedValueResolverAware,
        ResourceLoaderAware,
        ApplicationEventPublisherAware,
        MessageSourceAware,
        ApplicationContextAware,
        ServletContextAware,
        LoadTimeWeaverAware,
        ImportAware {

    @Override
    public void setBeanName(String name) {
        // BeanNameAware 作用：让 Bean 对 Name 有知觉
        // 这个方法只是简单的返回我们当前的 beanName, 听官方的意思是这个接口更多的使用在 spring 的框架代码中，实际开发环境应该不建议使用
        log.error("6.Aware - BeanNameAware 的 setBeanName 方法 - name={}", name);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        // 获取 Bean 的类装载器
        log.error("7.Aware - BeanClassLoaderAware 的 setBeanClassLoader 方法 - classLoader={}", classLoader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 获取 bean 工厂，beanFactory 让你可以不依赖注入方式，随意的读取 IOC 容器里面的对象，不过 beanFactory 本身还是要注入的
        // 注意：如果使用 @Configuration 注解的话，setBeanFactory 方法会执行 2 次
        log.error("8.Aware - BeanFactoryAware 的 setBeanFactory 方法 - beanFactory={}", beanFactory);
    }

    @Override
    public void setEnvironment(Environment environment) {
        // 获取 bean 工厂，beanFactory 让你可以不依赖注入方式，随意的读取 IOC 容器里面的对象，不过 beanFactory 本身还是要注入的
        log.error("9.Aware - EnvironmentAware 的 setEnvironment 方法 - environment={}", environment);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        // 通常我们使用 @Value 注解来获取 properties 和 yml 文件中的值，每个类中都要使用 @Value 也很繁琐，实现 EmbeddedValueResolverAware 接口后就方便多了
        // 用法也跟 @Value 一样，需要用 ${} 包裹住；
        log.error("10.Aware - EmbeddedValueResolverAware 的 setEmbeddedValueResolver 方法 - spring.datasource.url={}, stringValueResolver={}",
                stringValueResolver.resolveStringValue("${spring.datasource.url}"), stringValueResolver);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        // Spring ResourceLoader 为我们提供了一个统一的 getResource() 方法来通过资源路径检索外部资源
        // 从而将资源或文件(例如文本文件、XML文件、属性文件或图像文件)加载到Spring应用程序上下文中的不同实现
        // 其实说白了，就是用来加载外部资源的；方法中有个参数：ResourceLoader ，这个参数其实就是 ApplicationContext(spring 的上下文对象) - 可直接强转
        ApplicationContext applicationContext = (ApplicationContext) resourceLoader;
        log.error("11.Aware - ResourceLoaderAware 的 setResourceLoader 方法 - txtFileName={}, resourceLoader={}",
                resourceLoader.getResource("spring-bean-life-cycle.txt").getFilename(), resourceLoader);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        // ApplicationEventPublisherAware 是一个事件发布器的接口，使用这个接口，我们自己的 Service 就拥有了发布事件的能力
        // 用户注册后，不再是显示调用其他的业务 Service，而是发布一个用户注册事件
        // 那么在这里是发布事件，那就肯定有监听事件的接口，这个接口叫做 ApplicationListener ，只要实现 ApplicationListener 接口就可以接受发布的事件了
        // 接下来我们写一个示例来模拟发布事件和监听事件
        log.error("12.Aware - ApplicationEventPublisherAware 的 setApplicationEventPublisher 方法 - applicationEventPublisher={}", applicationEventPublisher);
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        // 国际化消息通知操作
        log.error("13.Aware - MessageSourceAware 的 setMessageSource 方法 - messageSource={}", messageSource);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // ApplicationContextAware 主要用来全局获取 ApplicationContext 上下文
        // ApplicationContext 其实就是容器，为此我们可以实现 ApplicationContextAware 接口来获取 ApplicationContext 容器对象
        // 我们可以把它做成一个公共的静态类，这样可以在任意地方想拿就拿了，也可以用 @Autowired 注入applicationContext
        log.error("14.Aware - ApplicationContextAware 的 setApplicationContext 方法 - applicationContext={}", applicationContext);
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        // 通过实现 ServletContextAware 接口可获取 servletContext，也就是 servlet 的上下文
        //
        // 什么是 ServletContext：WEB容器在启动时，它会为每个WEB应用程序都创建一个对应的ServletContext对象，它代表当前web应用
        // ServletConfig对象中维护了ServletContext对象的引用，开发人员在编写servlet时，可以通过ServletConfig.getServletContext方法获得ServletContext对象。
        //
        // 由于一个WEB应用中的所有Servlet共享同一个 ServletContext 对象，因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象
        log.error("15.Aware - ServletContextAware 的 setServletContext 方法 - servletContext={}", servletContext);
    }

    @Override
    public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
        // LoadTimeWeaver 简称LTW，LTW是AOP的一种实现方式，此方法是为了获取Aop织入的对象，使用的织入方式是：类加载期织入
        // 一般的aop都是运行期织入，就是在运行的时候才进行织入切面方法，但是LTW是在类加载前就被织入了，也就是class文件在jvm加载之前进行织入切面方法
        // 只有在使用 @EnableLoadTimeWeaving 或者存在 LoadTimeWeaver 实现的 Bean 时才会调用，顺序也很靠后
        log.error("16.Aware - LoadTimeWeaverAware 的 setLoadTimeWeaver 方法 - loadTimeWeaver={}", loadTimeWeaver);
    }

    @Override
    public void setImportMetadata(AnnotationMetadata annotationMetadata) {
        // 只有被其他配置类 @Import(XX.class) 时才会调用，这个调用对 XX.class 中的所有 @Bean 来说顺序是第 1 的
        log.error("17.Aware - ImportAware 的 setImportMetadata 方法 - annotationMetadata={}", annotationMetadata);
    }

}
